package top.goldenyear.porpoise.infra.storage.core.service.s3;

import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.Getter;
import org.noear.solon.Utils;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import top.goldenyear.porpoise.common.core.model.Ret;
import top.goldenyear.porpoise.infra.storage.config.FileConfig;
import top.goldenyear.porpoise.infra.storage.core.model.Media;
import top.goldenyear.porpoise.infra.storage.core.service.FileClient;
import top.goldenyear.porpoise.infra.storage.core.service.exception.PorpoiseFileException;
import top.goldenyear.porpoise.infra.storage.core.util.BucketUtils;

import static top.goldenyear.porpoise.infra.storage.core.enums.FileConstant.DEFAULT_BUCKET;

/**
 * S3存储实现
 *
 * @author airhead
 */
public class S3FileClient implements FileClient {
  private final String configId;
  private final String defaultBucket;
  @Getter
  private final S3Client client;
  private final S3Presigner presigner;

  public S3FileClient(String configId, FileConfig fileConfig) {
    this.configId = configId;
    this.defaultBucket = BucketUtils.justify(fileConfig.getBucket(), DEFAULT_BUCKET);
    this.client = BucketUtils.createClient(fileConfig);
    this.presigner = BucketUtils.createClientPresigner(fileConfig);
  }

  @Override
  public String getConfigId() {
    return configId;
  }

  @Override
  public boolean exists(String bucket, String key) throws PorpoiseFileException {
    bucket = BucketUtils.justify(bucket, defaultBucket);

    try {
      HeadObjectRequest headObjectRequest = HeadObjectRequest.builder().bucket(bucket).key(key).build();

      return client.headObject(headObjectRequest).sdkHttpResponse().isSuccessful();
    } catch (NoSuchKeyException e) {
      return false;
    } catch (Exception e) {
      throw new PorpoiseFileException(e);
    }
  }

  @Override
  public String getTempUrl(String bucket, String key, Duration duration) throws PorpoiseFileException {
    bucket = BucketUtils.justify(bucket, defaultBucket);

    try {
      GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(bucket).key(key).build();

      GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder().getObjectRequest(getObjectRequest).signatureDuration(duration).build();

      URL url = presigner.presignGetObject(presignRequest).url();
      return url != null ? url.toString() : null;
    } catch (Exception e) {
      throw new PorpoiseFileException(e);
    }
  }

  @Override
  public Media get(String bucket, String key) throws PorpoiseFileException {
    bucket = BucketUtils.justify(bucket, defaultBucket);

    try {
      // 使用 GetObjectRequest 而不是 GetUrlRequest
      GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(bucket).key(key).build();

      // 获取对象流
      ResponseInputStream<GetObjectResponse> responseInputStream = client.getObject(getObjectRequest);
      GetObjectResponse response = responseInputStream.response();

      // 获取响应的内容类型和内容大小
      String contentType = response.contentType();
      long contentSize = response.contentLength();

      // 返回新的 Media 对象，包含对象的输入流、内容类型和大小
      return new Media(responseInputStream, contentType, contentSize);
    } catch (Exception e) {
      throw new PorpoiseFileException(e);
    }
  }

  @Override
  public Ret put(String bucket, String key, Media media) throws PorpoiseFileException {
    bucket = BucketUtils.justify(bucket, defaultBucket);

    String streamMime = media.contentType();
    if (Utils.isEmpty(streamMime)) {
      streamMime = "text/plain; charset=utf-8";
    }

    try {
      // 构建 PutObjectRequest
      PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(bucket).key(key).contentType(streamMime).build();
      // 将 InputStream 转换为 RequestBody
      RequestBody requestBody = RequestBody.fromInputStream(media.body(), media.contentSize());

      // 上传对象
      PutObjectResponse resp = client.putObject(putObjectRequest, requestBody);

      return Ret.okWithData(resp);
    } catch (Exception ex) {
      throw new PorpoiseFileException(ex);
    }
  }


  @Override
  public Ret delete(String bucket, String key) throws PorpoiseFileException {
    bucket = BucketUtils.justify(bucket, defaultBucket);

    try {
      DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder().bucket(bucket).key(key).build();

      DeleteObjectResponse resp = client.deleteObject(deleteObjectRequest);
      return Ret.okWithData(resp);
    } catch (Exception e) {
      throw new PorpoiseFileException(e);
    }
  }

  @Override
  public Ret deleteBatch(String bucket, Collection<String> keys) throws PorpoiseFileException {
    bucket = BucketUtils.justify(bucket, defaultBucket);

    try {
      List<ObjectIdentifier> objectIdentifiers = new ArrayList<>();
      for (String key : keys) {
        objectIdentifiers.add(ObjectIdentifier.builder().key(key).build());
      }

      Delete delete = Delete.builder().objects(objectIdentifiers).build();

      DeleteObjectsRequest deleteObjectsRequest = DeleteObjectsRequest.builder().bucket(bucket).delete(delete).build();

      DeleteObjectsResponse resp = client.deleteObjects(deleteObjectsRequest);
      return Ret.okWithData(resp);
    } catch (Exception e) {
      throw new PorpoiseFileException(e);
    }
  }
}
